home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / e14.zip / E.ASM < prev    next >
Assembly Source File  |  1991-12-15  |  65KB  |  2,501 lines

  1. ;E 1.4.  Copyright (C) David Nye, 1990, 1991, all rights reserved.
  2.  
  3. IDEAL
  4. ;Constants
  5.  
  6. LINEWIDTH       EQU 80          ;Length of string storage for line
  7. SCREENLENGTH    EQU 24          ;Number of rows in display window
  8. MAXLINES        EQU 8096        ;Size of arrays of line pointers
  9. BUFFERLENGTH    EQU 1200h       ;Length of file buffer
  10. STACKSIZE       EQU 50h         ;Length of stack
  11. BELL            EQU 7           ;Some non-printing chars
  12. BS              EQU 8
  13. HT              EQU 9
  14. LF              EQU 10
  15. CR              EQU 13
  16. CRLF            EQU 0A0Dh
  17. CTRL_Z          EQU 26
  18. ESCAPE          EQU 27
  19. DEL             EQU 127
  20. PROGLENGTH = EndOfProgram - Orig + 100h  ;Length of .COM file
  21.  
  22.  
  23. ;Create a named string
  24. MACRO String Name, Text
  25.         LOCAL LL
  26. Name    db LL-Name-1, Text
  27. LL:
  28. ENDM
  29.  
  30. ;Make <Name> a byte ptr to <Length> bytes of storage after end of code.
  31. ;Like a .DATA? segment for .COM file, so uninitialized data doesn't take up
  32. ;room in the .COM file.
  33. MACRO B? Name, Length
  34.         LOCAL where
  35.         where = EndOfProgram + BuffersCount
  36. Name    EQU BYTE PTR where
  37.         BuffersCount = BuffersCount + Length
  38. ENDM
  39.  
  40. ;Like B? but for word storage
  41. MACRO W? Name, Length
  42.         LOCAL where
  43.         where = EndOfProgram + BuffersCount
  44. Name    EQU WORD PTR where
  45.         BuffersCount = BuffersCount + Length + Length
  46. ENDM
  47.  
  48. BuffersCount = 0
  49.  
  50.  
  51. MODEL TINY
  52. CODESEG
  53. ORG 100h
  54. Orig:
  55. jmp Start
  56.  
  57. String copyRight 'E 1.3.  Copyright (C) David Nye, 1991.  All rights reserved.'
  58.  
  59. ;Some defaults, use ECONFIG to change
  60.  
  61. attribNl        db 17h          ;Default text attributes
  62. attribInv       db 70h          ;Default status line attributes
  63. tabSize         db 4            ;Tab increment
  64. inserting?      db -1           ;True if in insert mode
  65. autoIndent?     db -1           ;True if in autoindent mode
  66. startInText?    db 0            ;Set to true to start up in text mode
  67. zLMargin        db 8            ;Left margin setting for Alt Z
  68. zRMargin        db LINEWIDTH-8  ;Right margin setting for Alt Z
  69.  
  70.  
  71. ;Strings
  72.  
  73. String cantOpenMsg,     "Can't open file."
  74. String rdErrorMsg,      'Error reading file.'
  75. String fileErrorMsg,    "Can't save file."
  76. String diskFullMsg,     'Disk full.'
  77. String noRoomMsg,       'Out of memory.'
  78. String sysErrMsg,       'System error.'
  79. String notMarkingMsg,   'Not marking.'
  80. String setLabelMsg,     'Label (0-9): '
  81. String setTabsMsg,      'Tab width: '
  82. String newFileMsg,      'File name: '
  83. String gotoMsg          'Jump to what line? '
  84. String editingMsg       <'Help F1',186,'Editing: '>
  85. String findMsg          'Find: '
  86. String replaceMsg       'Replace with: '
  87. String notFoundMsg      'No more matching strings found.'
  88. String anyKeyMsg        'Press any key to continue.'
  89. String ctrlCMsg         '*Break*'
  90. String cancelledMsg     'Cancelled.'
  91. BAK                     db  '.BAK', 0
  92. comspec$                db  'COMSPEC='
  93. helpMsg                 db 'CURSOR left           left arrow, ^S    '
  94.                         db 'BLOCK begin                 @B          '
  95.                         db '  right               right arrow, ^D   '
  96.                         db '  copy block to buffer      @C *        '
  97.                         db '  word left           ^left arrow, ^A   '
  98.                         db '  delete block to buffer    @D *        '
  99.                         db '  word right          ^right arrow, ^F  '
  100.                         db '  insert block from buffer  @I *        '
  101.                         db '  tab right, left     Tab, Shift Tab    '
  102.                         db '  empty block buffer        @E *        '
  103.                         db '  start, end of line  Home, End         '
  104.                         db '  unmark                    @U          '
  105.                         db '  line up             up arrow, ^E      '
  106.                         db 'FIND                        @F +        '
  107.                         db '  line down           down arrow, ^X    '
  108.                         db '  replace                   @R +        '
  109.                         db '  page up             PgUp, ^R          '
  110.                         db '  find/replace all          @A +        '
  111.                         db '  page down           PgDn, ^C          '
  112.                         db 'SAVE and continue           @S          '
  113.                         db '  start of file       ^PgUp             '
  114.                         db '  save and exit             @X, ESC     '
  115.                         db '  end of file         ^PgDn             '
  116.                         db '  toggle kill save at exit  @K          '
  117.                         db 'DELETE                Del               '
  118.                         db '  open another file         @O+         '
  119.                         db '  backspace           Backspace         '
  120.                         db 'JUMP to line #              @J          '
  121.                         db '  delete word left    ^[                '
  122.                         db '  set, goto label (0-9)     @L, @G      '
  123.                         db '  delete word right   ^], ^T            '
  124.                         db 'MARGIN set L, R             ^Home, ^End '
  125.                         db '  delete rest of line ^\                '
  126.                         db '  wrap paragraph            @W          '
  127.                         db '  delete line         ^-, ^Y            '
  128.                         db '  set tabs                  @T          '
  129.                         db '  undelete line       ^^                '
  130.                         db '  toggle autoindent         ^@          '
  131.                         db 'INSERT mode toggle    Ins               '
  132.                         db '  toggle text/prog mode     @Z          '
  133.                         db '  insert raw char     @= (+80h w shift) '
  134.                         db 'SHELL to DOS, run EFn.BAT   F2, F3-F6 * '
  135.                         db 80 dup (' ')
  136.                         db '@ = Alt, ^ = Ctrl, * = to/from file if s'
  137.                         db 'hifted, + = use last string if shifted. '
  138.                         db 'Status line flags:  Insert  Overwrite  C'
  139.                         db 'hanged  AutoIndent  [ LMargin  ] RMargin'
  140.  
  141.  
  142. ;EXEC function parameter block
  143.  
  144. EXECParams      dw 0
  145. EXECCmdLineOff  dw 0
  146. ProgramSegment  dw 0, -1, -1 , -1 , -1
  147. EXECBAT         db 0, '/c EF'
  148. EXECFnumber     db 'x.BAT '
  149. EXECFileName    db 20 dup (0)
  150.  
  151. ;Variables
  152.  
  153. newFile?        db 0            ;True if new file
  154. marking?        db 0            ;True if marking text
  155. changed?        db 0            ;True if original file has been changed
  156. isBAKed?        db 0            ;True if .BAK file already written
  157. needCopies?     db -1           ;True unless lines in buffer were just deleted
  158. autoReplace?    db 0            ;-1 if auto-replace with shift, 1 without shift
  159. noEscape?       db 0            ;True if prompt demands response
  160. labelTable      dw 10 dup (0)   ;Table of line pointers assigned to labels
  161.  
  162. ;These variables and buffers are allocated space following .COM file
  163. W? sstack, STACKSIZE            ;Stack goes here
  164. STACKTOP = EndOfProgram + BuffersCount
  165. W? cursorShape, 1               ;Line cursor parameters for color or mono
  166. B? fName?, 1                    ;True if file name given on command line
  167. B? justFound?, 1                ;True if no other commands since last Find
  168. B? swapped?, 1                  ;True if edited file swapped out during EXEC
  169. W? lMargin, 1                   ;Current margins
  170. W? rMargin, 1
  171. W? fHandle, 1                   ;File handle
  172. W? lastLine, 1                  ;Index of last line in file
  173. W? blockPtrsLast, 1             ;Index of last line in block buffer
  174. W? top, 1                       ;Index of first line on screen
  175. W? bottom, 1                    ;Index of last line on screen
  176. W? mark, 1                      ;Start of marking for block command
  177. W? here, 1                      ;Temporaries
  178. W? spTemp, 1
  179. W? comspecPtrOff, 1             ;Pointer to COMSPEC value: offset, segment
  180. W? comspecPtrSeg, 1
  181. W? bufferPtr, 1                 ;Multipurpose buffer pointer
  182. W? hereCol, 1
  183. W? topSegPtr, 1
  184. W? topSeg, 1
  185. W? topIndex, 1
  186. W? videoSegment, 1              ;Segment of system's video memory
  187. W? heapStart, 1                 ;Segment of start of heap
  188. W? heapPtr, 1                   ;Segment pointer to next free paragraph in heap
  189. B? fName, 50                    ;File name in ASCIIZ format
  190. B? otherFName, 50               ;Name of second file
  191. W? otherLine, 1                 ;Line of cursor in second file
  192. ESSENTIALS = BuffersCount       ;Buffers above here not saved with shell + swap
  193. W? temp, 1                      ;Temporary storage
  194. B? fNameBAK, LINEWIDTH          ;Current file with .BAK extension added
  195. B? fNameTemp, LINEWIDTH         ;File name for block read/writes, shell
  196. B? pad, LINEWIDTH               ;Scratch buffer
  197. B? findString, LINEWIDTH        ;Search string for Find command
  198. B? replaceString, LINEWIDTH     ;New string for Replace command
  199. W? linePtrs, MAXLINES           ;List of line pointers
  200. W? blockPtrs, MAXLINES          ;Line pointers for block or line deletes
  201. B? buffer, BUFFERLENGTH         ;File buffer
  202. ENDFBUFFER = BuffersCount
  203.  
  204. ;Jump tables:   ^ = Ctrl, @ = Alt, # = Shift.
  205. ctrlTable       dw na           ;Undefined
  206.                 dw WordLeft     ;^A
  207.                 dw na           ;^B
  208.                 dw PageDown     ;^C
  209.                 dw Right        ;^D
  210.                 dw Up           ;^E
  211.                 dw WordRight    ;^F
  212.                 dw na           ;^G or BEL
  213.                 dw BackSpace    ;^H or BS
  214.                 dw Tab          ;^I or HT
  215.                 dw na           ;^J or LF
  216.                 dw na           ;^K or VT
  217.                 dw na           ;^L or FF
  218.                 dw CRet         ;^M or CR
  219.                 dw na           ;^N or SO
  220.                 dw na           ;^O or SI
  221.                 dw na           ;^P
  222.                 dw na           ;^Q or DC1
  223.                 dw PageUp       ;^R or DC2
  224.                 dw Left         ;^S or DC3
  225.                 dw DeleteWordR  ;^T or DC4
  226.                 dw na           ;^U
  227.                 dw na           ;^V
  228.                 dw na           ;^W
  229.                 dw Down         ;^X or CAN
  230.                 dw DeleteLine   ;^Y
  231.                 dw na           ;^Z
  232.                 dw DeleteWordL  ;^[
  233.                 dw DeleteToEOL  ;^\
  234.                 dw DeleteWordR  ;^]
  235.                 dw UndeleteLine ;^^
  236.                 dw DeleteLine   ;^-
  237.  
  238. auxTable        dw 3 DUP (na)   ;Undefined
  239.                 dw AutoIndent   ;^@ or NUL
  240.                 dw 11 DUP (na)  ;Undefined
  241.                 dw ReverseTab   ;#Tab
  242.                 dw na           ;@Q
  243.                 dw Wrap         ;@W
  244.                 dw EmptyBuffer  ;@E
  245.                 dw Replace      ;@R
  246.                 dw SetTabs      ;@T
  247.                 dw na           ;@Y
  248.                 dw Unmark       ;@U
  249.                 dw InsertBlock  ;@I
  250.                 dw OtherFile    ;@O
  251.                 dw na           ;@P
  252.                 dw 4 DUP (na)   ;Undefined
  253.                 dw ReplaceAll   ;@A
  254.                 dw Save         ;@S
  255.                 dw DeleteBlock  ;@D
  256.                 dw Find         ;@F
  257.                 dw GotoLabel    ;@G
  258.                 dw Help         ;@H
  259.                 dw Jump         ;@J
  260.                 dw Kill         ;@K
  261.                 dw SetLabel     ;@L
  262.                 dw 5 DUP (na)   ;Undefined
  263.                 dw ToggleWPMode ;@Z
  264.                 dw Exit         ;@X
  265.                 dw Copy         ;@C
  266.                 dw na       ;@V
  267.                 dw BeginBlock   ;@B
  268.                 dw na           ;@N
  269.                 dw na           ;@M
  270.                 dw 8 DUP (na)   ;Undefined
  271.                 dw Help         ;F1
  272.                 dw Shell        ;F2
  273.                 dw F3BAT        ;F3
  274.                 dw F4BAT        ;F4
  275.                 dw F5BAT        ;F5
  276.                 dw F6BAT        ;F6
  277.                 dw na           ;F7
  278.                 dw na           ;F8
  279.                 dw na           ;F9
  280.                 dw na           ;F10
  281.                 dw 2 DUP (na)   ;Undefined
  282.                 dw HomeLine     ;Home
  283.                 dw Up           ;Up arrow
  284.                 dw PageUp       ;PgUp
  285.                 dw na           ;Undefined
  286.                 dw Left         ;Left arrow
  287.                 dw na           ;Undefined
  288.                 dw Right        ;Right arrow
  289.                 dw na           ;Undefined
  290.                 dw EndLine      ;End
  291.                 dw Down         ;Down arrow
  292.                 dw PageDown     ;PgDn
  293.                 dw ToggleIns    ;Ins
  294.                 dw Delete       ;Del
  295.                 dw na           ;#F1
  296.                 dw Shell        ;#F2
  297.                 dw F3BAT        ;#F3
  298.                 dw F4BAT        ;#F4
  299.                 dw F5BAT        ;#F5
  300.                 dw F6BAT        ;#F6
  301.                 dw na           ;#F7
  302.                 dw na           ;#F8
  303.                 dw na           ;#F9
  304.                 dw na           ;#F10
  305.                 dw 20 DUP (na)  ;[^Fn, @Fn]
  306.                 dw na           ;^PrtSc
  307.                 dw WordLeft     ;^Left arrow
  308.                 dw WordRight    ;^Right arrow
  309.                 dw SetRMargin   ;^End
  310.                 dw BottomFile   ;^PgDn
  311.                 dw SetLMargin   ;^Home
  312.                 dw 10 DUP (na)  ;[Alt numbers]
  313.                 dw na           ;@-
  314.                 dw InsertRaw    ;@=
  315.                 dw TopFile      ;^PgUp
  316.  
  317. ;******************************************************************************
  318.  
  319. Start:
  320.   mov ax, cs
  321.   mov [ProgramSegment], ax      ;Store current segment for EXEC function
  322.   add ax, ((PROGLENGTH + ENDFBUFFER) SHR 4) + 1
  323.   mov [heapStart], ax           ;Compute start of free memory in paragraphs
  324.   mov [heapPtr], ax
  325.   mov sp, OFFSET STACKTOP
  326.   mov si, 80h                   ;Make pointer to command tail
  327.   mov cl, [si]                  ;Get filename length
  328.   sub ch, ch
  329.   mov [fName?], cl              ;Save a copy
  330.   mov al, ' '                   ;Skip leading blanks
  331. @@L1:
  332.   inc si
  333.   cmp al, [si]
  334.   loope @@L1
  335.   inc cx
  336.   mov di, OFFSET fName          ;Move command tail to FName
  337.   rep movsb
  338.   sub al, al                    ;Make ASCIIZ string
  339.   stosb
  340.   mov ax, 2523h                 ;Redirect Ctrl C handler
  341.   mov dx, OFFSET Cancel
  342.   int 21h
  343.   mov [byte OtherFName], 0      ;Prevent Alt Shift O crash without prev. file
  344.   mov ah, 0Fh                   ;Set defaults for color or mono adapter
  345.   int 10h
  346.   mov bx, 0B000h                ;If mode 7 (= MDA or Herc), video seg=B000h,
  347.   mov dx, 0C0Dh                 ;Set cursor shapes for mono
  348.   cmp al, 7
  349.   je @@L2
  350.   mov bx, 0B800h                ;Otherwise video seg=B800h,
  351.   mov dx, 0607h                 ;Color cursor size
  352. @@L2:
  353.   mov es,bx                     ;Improved DesqView-compatible code, furnished
  354.   push bx                       ; by gchicares on BIX
  355.   push dx
  356.   mov cx,'DE'
  357.   mov dx,'SQ'
  358.   mov ax,2b01h
  359.   int 21h
  360.   pop dx
  361.   pop bx
  362.   cmp al,0ffh
  363.   je @@L3a
  364.   mov ah,0feh
  365.   int 10h
  366.   mov bx,es
  367. @@L3a:
  368.   mov [videoSegment], bx
  369.   mov [cursorShape], dx
  370.   mov es, [2Ch]                 ;Find COMSPEC
  371.   sub di, di
  372. @@L3:
  373.   mov si, OFFSET comspec$
  374.   mov cx, 8
  375.   repe cmpsb
  376.   jne @@L3
  377.   mov [comspecPtrOff], di
  378.   mov [comspecPtrSeg], es
  379.   mov es, [programSegment]
  380.  
  381. InitFile:
  382.   mov dx, OFFSET fName          ;Open file and set up list of line pointers
  383.   cmp [fName?], 0               ;If no file name specified on command line,
  384.   jne @@L0
  385.   mov [noEscape?], -1           ;Prompt for it
  386.   call GetFileName
  387. @@L0:
  388.   call OpenFile
  389.   mov [lMargin], 0              ;Set initial margins
  390.   mov [rMargin], LINEWIDTH - 1
  391.   cmp [startInText?], 0
  392.   je NextKey
  393.   call ToggleWPMode
  394.  
  395. NextKey:
  396.   call Redraw                   ;Redraw screen, status line
  397. NextNoRedraw:
  398.   call DrawCursor               ;Place cursor
  399.   sub ah, ah                    ;Get keypress to AL
  400.   int 16h
  401.   or al, al                     ;Check for control codes
  402.   je IsAux
  403.   cmp al, ' '
  404.   jb IsCtrl
  405.   call Insert                   ;Insert or overwrite if none
  406.   jmp NextKey
  407.  
  408. IsAux:
  409.   xchg al, ah                   ;Get aux code
  410.   cmp al, 132
  411.   ja NextKey
  412.   mov si, OFFSET auxTable       ;Jump indirect to appropriate routine
  413. DoTableJump:
  414.   shl ax, 1
  415.   add si, ax
  416.   call [WORD si]
  417.   jmp NextKey
  418.  
  419. IsCtrl:
  420.   mov si, OFFSET ctrlTable      ;Jump to routine through table
  421.   sub ah, ah
  422.   jmp DoTableJump
  423.  
  424. InsertRaw:
  425.   call Shifted?                 ;Set flag if shifted
  426.   mov dl, al
  427.   sub ah, ah                    ;Get keypress to AL
  428.   int 16h
  429.   or al, al                     ;Check for aux code, ignore InsertRaw if found
  430.   je IsAux
  431.   cmp dl, 0                     ;If shift was down set high bit
  432.   jz @@L1
  433.   or al, 80h
  434. @@L1:
  435.   call Insert                   ;Insert
  436.   jmp NextKey
  437.  
  438. SwapNames:
  439. ;Swap old and new file names prior to opening new file
  440.   mov si, OFFSET fName
  441.   mov di, OFFSET otherFName
  442.   mov cx, 50
  443. @@L1:
  444.   mov al, [si]
  445.   xchg al, [di]
  446.   mov [si], al
  447.   inc si
  448.   inc di
  449.   loop @@L1
  450.   ret
  451.  
  452. OtherFile:
  453. ;Open another file
  454.   call Shifted?                 ;Set flag if shifted
  455.   mov dh, [byte OtherFName]     ;Ignore it if no prior file
  456.   or dh, dh
  457.   jz @@L1
  458.   mov dh, al
  459. @@L1:
  460.   call Save                     ;Save current file if altered
  461.   mov ax, [blockPtrsLast]       ;If block buffer is empty,
  462.   sub ax, OFFSET blockPtrs
  463.   jne @@L2
  464.   mov ax, [heapStart]           ; reset heap pointer to start
  465.   mov [heapPtr], ax
  466.   jmp SHORT GetOther            ; prompt for new file name
  467. @@L2:
  468.   shr ax, 1                     ;Else move lines with pointers in block buffer
  469.   mov cx, ax                    ; to start of heap (load new file above them)
  470.   mov dl, 5
  471.   mul dl
  472.   add ax, [heapStart]           ;Calculate upper limit of target zone
  473.   mov [heapPtr], ax             ; which will also be new value of heap pointer
  474.   mov bx, OFFSET blockPtrs      ;For each pointer in block buffer,
  475. @@L3:
  476.   cmp [bx], ax                  ;If its line is already within target zone,
  477.   jae @@L4
  478.   mov es, [bx]                  ;Set high bit of first char in line to mark it
  479.   or [byte es:0], 80h           ; (we won't need to move these lines)
  480. @@L4:
  481.   inc bx                        ;Next pointer
  482.   inc bx
  483.   loop @@L3
  484.   push ds
  485.   mov bx, OFFSET blockPtrs      ;For each pointer in block buffer:
  486.   mov es, [heapStart]
  487. @@L4a:
  488.   test [byte es:0], 80h         ;If high bit set in target line,
  489.   jne @@L7                      ; line already in use, try next target line
  490.   mov ds, [cs:bx]               ;If high bit set in source line,
  491.   test [byte 0], 80h
  492.   je @@L5                       ; don't need to move it (already there)
  493.   inc bx                        ; Next source line, same target line
  494.   inc bx
  495.   jmp SHORT @@L8
  496. @@L5:
  497.   mov cx, 40                    ;Else move one line
  498.   sub si, si
  499.   sub di, di
  500.   rep movsw
  501.   mov [cs:bx], es               ;Update pointer
  502. @@L6:
  503.   inc bx                        ;Next block pointer
  504.   inc bx
  505. @@L7:
  506.   mov ax, es                    ;Next target line
  507.   add ax, 5
  508.   mov es, ax
  509. @@L8:
  510.   segcs                         ;Loop until all lines moved
  511.   cmp bx, [blockPtrsLast]
  512.   jb @@L4a
  513.   pop ds
  514.   mov bx, OFFSET blockPtrs      ;Reset high bits of all first chars set high
  515. @@L9:
  516.   mov es, [bx]
  517.   and [byte es:0], 7Fh
  518.   inc bx
  519.   inc bx
  520.   cmp bx, [blockPtrsLast]
  521.   jb @@L9
  522. GetOther:
  523.   call SwapNames
  524.   cmp dh, 0                     ;If Shifted, use last file
  525.   je @@L1
  526.   mov dx, OFFSET fName
  527.   call OpenFile1
  528.   mov bx, [otherLine]           ;Restore cursor to previous position
  529.   mov ax, [here]
  530.   mov [otherLine], ax
  531.   jmp NewLine
  532. @@L1:
  533.   mov [noEscape?], -1
  534.   mov ax, [here]                ;Save current file name and cursor line
  535.   mov [otherLine], ax
  536.   mov dx, OFFSET fName          ;Prompt for new file name
  537.   call GetFileName
  538.   jmp SHORT OpenFile1           ;Read in new file above block buffer lines
  539.  
  540. OpenFile:
  541. ;Open file, load if found, then close.  Call with dx -> ASCIIZ file name.
  542.   mov ax, OFFSET blockPtrs      ;Reset block buffer pointer
  543.   mov [blockPtrsLast], ax
  544.   mov ax, [heapStart]           ;Begin loading at start of heap
  545.   mov [heapPtr], ax
  546. OpenFile1:
  547.   mov [newFile?], 0
  548.   mov [changed?], 0
  549.   mov ax, 3D00h                 ;Try to open file
  550.   int 21h
  551.   jnc OldFile
  552.   mov [newFile?], -1            ;If no such file, remind me to create a new one
  553. OpenNewFile:
  554.   call NewFile
  555.   jmp SHORT XOpenFile
  556. OldFile:
  557.   mov [fHandle], ax             ;Else save file handle
  558.   mov bx, OFFSET linePtrs       ;Read file in
  559.   mov dx, ax
  560.   call ReadFile
  561.   dec bx
  562.   dec bx
  563.   mov [lastLine], bx            ;Save index of last line
  564.   mov bx, [fHandle]             ;Close file
  565.   mov ah, 3Eh
  566.   int 21h
  567. XOpenFile:
  568.   mov bx, OFFSET linePtrs       ;Reset row, screen pointers
  569.   mov [top], bx
  570.   mov es, [bx]
  571.   sub di, di
  572.   ret
  573.  
  574. GetFileName:
  575. ;Prompt for file name.  Abort if null name.  Call with buffer address in DX.
  576.   push si
  577.   push dx
  578.   mov si, OFFSET newFileMsg     ;Print prompt
  579.   call Prompt
  580.   pop dx
  581.   call GetString                ;Get file name
  582.   mov si, dx                    ;Convert to ASCIIZ
  583.   add si, ax
  584.   mov [BYTE si], 0
  585.   pop si
  586.   ret
  587.  
  588. GetString:
  589. ;Get string to [DX], return count (minus CR/LF) in AX.  Abort if null string.
  590.   push bx
  591.   push cx
  592.   push si
  593.   push di
  594.   push es
  595.   push dx
  596. @@L2:
  597.   mov dx, OFFSET pad            ;Get string
  598.   mov ah, 3Fh
  599.   sub bx, bx
  600.   mov cx, 20
  601.   int 21h
  602.   dec ax                        ;Strip CR/LF
  603.   dec ax
  604.   jnz @@L1                      ;Abort if null string
  605.   cmp [noEscape?], 0            ; unless escape precluded
  606.   je @@L0
  607.   call Beep
  608.   mov si, OFFSET newFileMsg
  609.   call Prompt
  610.   jmp @@L2
  611. @@L0:
  612.   mov si, OFFSET cancelledMsg
  613.   jmp Abort
  614. @@L1:
  615.   mov cx, ax                    ;Copy temporary copy of string to [DX]
  616.   pop dx
  617.   push ax
  618.   mov ax, ds
  619.   mov es, ax
  620.   mov si, OFFSET pad
  621.   mov di, dx
  622.   rep movsb
  623.   pop ax
  624.   pop es
  625.   pop di
  626.   pop si
  627.   pop cx
  628.   pop bx
  629.   mov [noEscape?], 0
  630.   ret
  631.  
  632. ReadFile:
  633. ;Load file with handle in DX, assigning segment pointers starting at BX
  634.   push es
  635.   mov ax, [heapPtr]
  636.   mov es, ax
  637.   sub cx, cx
  638.   sub di, di
  639. FillBuffer:
  640.   push bx                       ; Fill buffer
  641.   push cx
  642.   push dx
  643.   mov bx, dx
  644.   mov ah, 3Fh
  645.   mov cx, BUFFERLENGTH
  646.   mov dx, OFFSET buffer
  647.   int 21h
  648.   jnc @@L1                      ; Check for read error
  649.   jmp ReadError
  650. @@L1:
  651.   pop dx
  652.   pop cx
  653.   pop bx
  654.   mov si, OFFSET buffer         ; Set pointers
  655.   add ax, si
  656.   mov [bufferPtr], ax
  657.   cmp ax, OFFSET buffer         ;Exit if empty buffer
  658.   je EndOfFile
  659.   cmp [byte si], LF             ;Skip LF if first char in buffer
  660.   jne SHORT NextLine
  661.   inc si
  662. NextLine:
  663.   mov al, [si]                  ;Get next char
  664.   cmp al, CR                    ;If char is CR, end of line
  665.   jne @@L2
  666.   inc si                        ; move past CR
  667.   cmp [byte si], LF             ; and LF if present
  668.   jne @@L1
  669.   inc si
  670. @@L1:
  671.   call EndOfLine                ; pad out line with spaces and save it
  672.   jmp SHORT @@L3
  673. @@L2:
  674.   cmp al, HT                    ;Else if a tab, expand it
  675.   jne @@L2a
  676.   push cx
  677.   mov al, ' '
  678.   mov cl, [tabSize]
  679.   sub ch, ch
  680.   rep stosb
  681.   pop cx
  682.   sub cl, [tabSize]
  683.   sbb ch, 0
  684.   inc si
  685.   jmp SHORT @@L3
  686. @@L2a:
  687.   movsb                         ;Else add char to line
  688.   dec cx
  689. @@L3:
  690.   cmp si, [bufferPtr]           ;Loop until end of buffer
  691.   jb NextLine
  692.   cmp si, OFFSET ENDFBUFFER     ;If buffer less than full, indicates end of file
  693.   jae FillBuffer
  694. EndOfFile:
  695.   call EndOfLine                ;Finish up present line
  696.   mov [heapPtr], es             ;Update pointer to start of free heap space
  697.   pop es
  698.   ret
  699.  
  700. EndOfLine:
  701.   add cx, LINEWIDTH             ;Pad to end with spaces
  702.   jle @@L1                      ;Truncate lines longer than LINEWIDTH chars
  703.   mov al, ' '
  704.   rep stosb
  705. @@L1:
  706.   mov [bx], es                  ;Store segment of this line
  707.   mov ax, es                    ;Next line
  708.   add ax, 5
  709.   cmp ax, 0A000h                ;Out of room?
  710.   jb SHORT @@L2
  711.   jmp NoRoom
  712. @@L2:
  713.   mov es, ax
  714.   inc bx
  715.   inc bx
  716.   sub di, di
  717.   sub cx, cx
  718. Ret3:
  719.   ret
  720.  
  721. Redraw:
  722. ;Redraw screen and status line
  723.   mov [here], bx
  724.   mov [hereCol], di
  725.   push bx
  726.   push di
  727.   push ds
  728.   push es
  729.   push di
  730.   mov es, [videoSegment]        ;Get segment for display
  731.   mov si, OFFSET editingMsg     ;Refresh status line:  "Editing ..."
  732.   call Prompt
  733.   mov di, 34                    ;Tab to column 17
  734.   mov si, OFFSET fName          ; <file name>
  735.   mov ah, [attribInv]
  736. @@S1:
  737.   lodsb
  738.   or al, al
  739.   je @@S2
  740.   stosw
  741.   jmp @@S1
  742. @@S2:
  743.   add di, 6                     ;3 spaces
  744.   mov al, 'L'                   ;"L" <line #>
  745.   stosw
  746.   inc di
  747.   inc di
  748.   mov ax, bx
  749.   sub ax, OFFSET linePtrs
  750.   shr ax, 1
  751.   inc ax
  752.   call PrintInt
  753.   add di, 4                     ;2 spaces
  754.   mov al, 'C'                   ;"C" <column number>
  755.   mov ah, [attribInv]
  756.   stosw
  757.   inc di
  758.   inc di
  759.   pop ax                        ;Get copy of DI as cursor row
  760.   inc ax
  761.   call PrintInt
  762.   mov di, LINEWIDTH * 2 - 12    ;Tab to start of status chars display
  763.   mov al, 'I'                   ;Insert/Overwrite status
  764.   mov ah, [attribInv]
  765.   test [inserting?], -1
  766.   jne @@S3
  767.   mov al, 'O'
  768. @@S3:
  769.   stosw
  770.   mov al, 'C'                   ;Changed status
  771.   test [changed?], -1
  772.   jne @@S5
  773.   mov al, ' '
  774. @@S5:
  775.   stosw
  776.   mov al, 'A'                   ;Autoinsert status
  777.   test [autoIndent?], -1
  778.   jne @@S6
  779.   mov al, ' '
  780. @@S6:
  781.   stosw
  782.   mov al, ' '                   ;L margin set?
  783.   cmp [lMargin], 0
  784.   je @@S7
  785.   mov al, '['
  786. @@S7:
  787.   stosw
  788.   mov al, ' '                   ;R margin set?
  789.   cmp [rMargin], LINEWIDTH - 1
  790.   je @@S8
  791.   mov al, ']'
  792.   stosw
  793. @@S8:
  794.   segcs
  795.   mov al, [attribNl]            ;Mark margins as non-inv chars in status line
  796.   mov di, [lMargin]
  797.   add di, di
  798.   je @@S8a
  799.   inc di
  800.   stosb
  801. @@S8a:
  802.   mov di, [rMargin]
  803.   cmp di, LINEWIDTH - 1
  804.   je @@S8b
  805.   add di, di
  806.   inc di
  807.   stosb
  808. @@S8b:
  809.   mov di, LINEWIDTH * 2         ;Move to next display line
  810.   mov ax, [top]                 ;Compute bottom of screen
  811.   mov bx, ax
  812.   add ax, (SCREENLENGTH - 1) * 2
  813.   cmp ax, [lastLine]            ;If at end of file,
  814.   jle @@L0
  815.   mov ax, [lastLine]            ; stop at lastLine
  816. @@L0:
  817.   mov [bottom], ax
  818. @@L1:                           ;For each row
  819.   mov cx, LINEWIDTH             ;Count of chars per row
  820.   mov ds, [cs:bx]               ;Get pointer to screen line
  821.   sub si, si                    ;Initialize column counter
  822.   segcs
  823.   mov ah, [attribNl]            ;Attribute = inverse video if Marked
  824.   test [cs:marking?], -1
  825.   je @@L2
  826.   segcs
  827.   cmp bx, [mark]
  828.   je @@L1b
  829.   jb @@L1a
  830.   segcs
  831.   cmp bx, [here]
  832.   jbe @@L1b
  833.   jmp SHORT @@L2
  834. @@L1a:
  835.   segcs
  836.   cmp bx, [here]
  837.   jb @@L2
  838. @@L1b:
  839.   segcs
  840.   mov ah, [attribInv]
  841. @@L2:                           ;For each char, write char and attribute
  842.   lodsb
  843.   stosw
  844.   loop @@L2                     ;Next char
  845.   inc bx                        ;Next row
  846.   inc bx
  847.   segcs
  848.   cmp bx, [bottom]              ;Stop if screen full
  849.   jle @@L1
  850.   mov cx, LINEWIDTH * 2 * (SCREENLENGTH + 1)  ;Fill out screen with blanks
  851.   sub cx, di
  852.   shr cx, 1
  853.   segcs
  854.   mov ah, [attribNl]
  855.   mov al, ''
  856.   rep stosw
  857. @@L3:
  858.   pop es
  859.   pop ds
  860.   pop di
  861.   pop bx
  862.   ret
  863.  
  864. DrawCursor:
  865. ;Set cursor shape and place it on screen
  866.   push bx
  867.   mov cx, [cursorShape]         ;Set cursor shape: line for insert mode,
  868.   test [Inserting?], -1
  869.   jne @@L1
  870.   sub ch, ch                    ;Block for overwrite
  871. @@L1:
  872.   mov ah, 1
  873.   int 10h
  874.   sub bx, [top]                 ;Show cursor at current row, column
  875.   shr bx, 1
  876.   inc bx
  877.   mov dh, bl
  878.   mov ax, di
  879.   mov dl, al
  880.   mov ah, 2
  881.   mov bh, 0
  882.   int 10h
  883.   pop bx
  884.   ret
  885.  
  886. Print0:
  887.   call ClearStatus              ;Blank status line
  888.   sub di, di                    ;Starting at beginning of line ...
  889.  
  890. Print:
  891. ;Print string pointed to by SI on status line in inverse video, starting at DI
  892.   push es
  893.   mov es, [videoSegment]
  894.   lodsb                         ;Get count of string to be printed
  895.   mov cl, al
  896.   sub ch, ch
  897.   mov ah, [attribInv]            ;Attribute = inverse video
  898. @@L1:
  899.   lodsb
  900.   stosw
  901.   loop @@L1
  902.   pop es
  903.   ret
  904.  
  905. ClearStatus:
  906. ;Inverse-blank status line
  907.   push di
  908.   push es
  909.   mov es, [videoSegment]
  910.   mov ah, [attribInv]
  911.   mov al, ' '
  912.   mov cx, 80
  913.   sub di, di
  914.   rep stosw
  915.   pop es
  916.   pop di
  917.   ret
  918.  
  919. Cancel:
  920. ;Ctrl C routine
  921.   mov si, OFFSET ctrlCMsg       ;Abort with message ...
  922.  
  923. Abort:
  924. ;Print counted string pointed to by SI on status line and abort
  925.   call Print0                   ;Print error message ...
  926.  
  927. na:
  928. ;Unassigned key or other error.  Beep and abort.
  929.   call Beep                     ;Beep
  930.   mov sp, OFFSET STACKTOP       ;Reset stack pointer to top
  931.   mov bx, [here]                ;Retrieve cursor position in case it was trashed
  932.   mov di, [hereCol]
  933.   call DrawCursor
  934.   jmp NextNoRedraw              ;Restart main editing loop
  935.  
  936. Beep:
  937.   mov ah, 2                     ;Output a BELL
  938.   mov dl, BELL
  939.   int 21h
  940.   ret
  941.  
  942. Prompt:
  943.   push di
  944.   call Print0                   ;Print string at start of line
  945.   mov dx, di                    ;Set cursor to end of printed string ...
  946.   shr dl, 1
  947.   sub dh, dh
  948.   pop di
  949.  
  950. GotoXY:
  951. ;Position cursor at row, column given by DL, DH
  952.   push bx
  953.   mov ah, 2
  954.   sub bx, bx
  955.   int 10h
  956.   pop bx
  957.   ret
  958.  
  959. PrintInt:
  960. ;Print to ES:DI in inverse video the unsigned decimal integer in AX
  961.   sub dx, dx                    ;Start stack with a null
  962.   push dx
  963.   mov cx, 10                    ;Get remainders of successive divisions by 10
  964. @@L1:
  965.   div cx
  966.   add dl, '0'                   ;Convert to ASCII
  967.   mov dh, [attribInv]           ;Attribute is reverse video
  968.   push dx
  969.   sub dx, dx
  970.   or ax, ax
  971.   jne @@L1
  972.   pop ax                        ;Pop and print remainders in reverse order
  973. @@L2:
  974.   stosw
  975.   pop ax
  976.   or ax, ax
  977.   jne @@L2
  978.   ret
  979.  
  980. NewLineC:
  981. ;Jump here if file is changed by a command
  982.   mov [changed?], -1
  983. NewLine:
  984.   mov [justFound?], 0
  985. NewLine0:
  986.   cmp bx, OFFSET linePtrs       ;Check bounds, adjust if necessary
  987.   jge @@L1
  988.   mov bx, OFFSET linePtrs
  989. @@L1:
  990.   cmp bx, [lastLine]
  991.   jle @@L2
  992.   mov bx, [lastLine]
  993. @@L2:
  994.   mov ax, [top]
  995.   cmp bx, ax
  996.   jge @@L3
  997.   mov [top], bx
  998. @@L3:
  999.   add ax, (SCREENLENGTH-1)*2
  1000.   cmp bx, ax
  1001.   jle @@L4
  1002.   mov ax, bx
  1003.   sub ax, (SCREENLENGTH-1)*2
  1004.   mov [top], ax
  1005. @@L4:
  1006.   mov es, [bx]                  ;Adjust ES to point to new line
  1007.   ret
  1008.  
  1009. Left:
  1010.   or di, di                     ;If at start of line,
  1011.   jne @@L1
  1012.   call Up                       ; move to end of line above
  1013.   jmp EndLine
  1014. @@L1:
  1015.   dec di                        ; else just decrement cursor
  1016. CursorMoved:
  1017.   mov [justFound?], 0
  1018.   ret
  1019.  
  1020. Right:
  1021.   cmp di, LINEWIDTH - 1         ;If at end of line,
  1022.   jne @@L1
  1023.   sub di, di                    ; move to start of line below
  1024.   jmp Down
  1025. @@L1:
  1026.   inc di                        ; else just increment cursor
  1027.   jmp SHORT CursorMoved
  1028.  
  1029. LScanE:
  1030. ;Scan left past first non-space or start of line
  1031.   mov al, ' '
  1032.   mov cx, di
  1033.   inc cx
  1034.   std
  1035.   repe scasb
  1036.   cld
  1037.   ret
  1038.  
  1039. LScanNE:
  1040. ;Scan left past first space or start of line
  1041.   mov al, ' '
  1042.   mov cx, di
  1043.   inc cx
  1044.   std
  1045.   repne scasb
  1046.   cld
  1047.   ret
  1048.  
  1049. RScanE:
  1050. ;Scan right past first non-space or end of line
  1051.   mov al, ' '
  1052.   mov cx, LINEWIDTH
  1053.   sub cx, di
  1054.   repe scasb
  1055.   ret
  1056.  
  1057. RScanNE:
  1058. ;Scan right past first space or end of line
  1059.   mov al, ' '
  1060.   mov cx, LINEWIDTH
  1061.   sub cx, di
  1062.   repne scasb
  1063.   ret
  1064.  
  1065. WordLeft:
  1066. ;Move left one word
  1067.   or di, di                     ;Do nothing if at start of line
  1068.   je @@Lx
  1069.   mov [justFound?], 0
  1070.   dec di                        ;Else starting at char to left,
  1071.   call LScanE                   ; skip spaces until non-space
  1072.   inc di
  1073.   je @@Lx                       ; or start of line,
  1074.   call LScanNE                  ; then scan to next space or start of line
  1075.   jne @@L1
  1076.   inc di
  1077. @@L1:
  1078.   inc di
  1079. @@Lx:
  1080.   ret
  1081.  
  1082. WordRight:
  1083. ;Move right one word
  1084.   cmp di, LINEWIDTH - 1         ;Do nothing if at end of line
  1085.   je @@Lx
  1086.   mov [justFound?], 0
  1087.   call RScanNE                  ;Skip non-spaces until space
  1088.   jne @@L1                      ; or end of line,
  1089.   dec di
  1090.   call RScanE                   ; then scan to next non-space or end of line
  1091. @@L1:
  1092.   dec di
  1093. @@Lx:
  1094.   ret
  1095.  
  1096. HomeLine:
  1097. ;Move cursor to L margin
  1098.   mov di, [lMargin]
  1099.   mov [justFound?], 0
  1100.   ret
  1101.  
  1102. EndLine:
  1103. ;Move cursor to R margin or end of text on line (but not left of L margin)
  1104.   push cx
  1105.   mov [justFound?], 0
  1106.   mov di, [rMargin]             ;Start at R margin
  1107.   cmp [BYTE es:di], ' '         ;If non-blank, stop here
  1108.   jne @@L2
  1109.   call LScanE                   ;Skip all spaces until non-space
  1110.   je @@L1                       ; or beginning of line
  1111.   inc di
  1112. @@L1:
  1113.   inc di
  1114. @@L2:
  1115.   cmp di, [lMargin]             ;If left of L margin, set to L margin
  1116.   jae @@L3
  1117.   mov di, [lMargin]
  1118. @@L3:
  1119.   pop cx
  1120. Ret2:
  1121.   ret
  1122.  
  1123. Up:
  1124. ;Move cursor up one line
  1125.   cmp bx, OFFSET linePtrs       ;If at top of file already, do nothing
  1126.   je Ret2
  1127.   dec bx
  1128.   dec bx
  1129.   jmp NewLine
  1130.  
  1131. Down:
  1132. ;Move cursor down one line
  1133.   cmp bx, [lastLine]            ;If at last line already, do nothing
  1134.   je Ret2
  1135.   inc bx
  1136.   inc bx
  1137.   jmp NewLine
  1138.  
  1139. PageUp:
  1140. ;Move cursor up one page
  1141.   sub bx, (SCREENLENGTH-1)*2
  1142.   jmp NewLine
  1143.  
  1144. PageDown:
  1145. ;Move cursor down one page
  1146.   add bx, (SCREENLENGTH-1)*2
  1147.   jmp NewLine
  1148.  
  1149. TopFile:
  1150. ;Move cursor to top of file
  1151.   mov bx, OFFSET linePtrs
  1152.   mov [top], bx
  1153.   call HomeLine
  1154.   jmp NewLine
  1155.  
  1156. BottomFile:
  1157. ;Move cursor to bottom of file
  1158.   mov bx, [lastLine]
  1159.   mov es, [bx]
  1160.   mov ax, bx
  1161.   sub ax, (SCREENLENGTH-1)*2
  1162.   cmp ax, OFFSET linePtrs
  1163.   ja @@L1
  1164.   mov ax, OFFSET linePtrs
  1165. @@L1:
  1166.   mov [top], ax
  1167.   call EndLine
  1168.   jmp NewLine
  1169.  
  1170. Tab:
  1171. ;Tab right
  1172.   mov [justFound?], 0
  1173.   mov ax, di                    ;Advance di to next tab stop
  1174.   mov cl, [tabSize]
  1175.   div cl
  1176.   sub cl, ah
  1177.   sub ch, ch
  1178.   add di, cx
  1179.   cmp di, LINEWIDTH             ;If past end of line,
  1180.   jl @@L1
  1181.   mov di, LINEWIDTH - 1         ;Set cursor at end of line
  1182. @@L1:
  1183.   ret
  1184.  
  1185. ReverseTab:
  1186. ;Tab left
  1187.   mov [justFound?], 0
  1188.   mov ax, di                    ;Decrement di to nearest tab stop
  1189.   dec al
  1190.   div [tabSize]
  1191.   mov al, ah
  1192.   sub ah, ah
  1193.   inc al
  1194.   sub di, ax
  1195.   jnc @@L1                      ;Set to start of line if past start
  1196.   sub di, di
  1197. @@L1:
  1198.   ret
  1199.  
  1200. SetLMargin:
  1201. ;Toggle left margin between 0 and present cursor setting
  1202.   cmp [lMargin], 0
  1203.   je @@L1
  1204.   mov [lMargin], 0
  1205.   ret
  1206. @@L1:
  1207.   mov [lMargin], di
  1208.   ret
  1209.  
  1210. SetRMargin:
  1211. ;Toggle right margin between LINEWIDTH-1 and present cursor setting
  1212.   cmp [rMargin], LINEWIDTH-1
  1213.   je @@L1
  1214.   mov [rMargin], LINEWIDTH-1
  1215.   ret
  1216. @@L1:
  1217.   mov [rMargin], di
  1218.   ret
  1219.  
  1220. ToggleWPMode:
  1221. ;Toggle word processing and programming defaults
  1222.   cmp [lMargin], 0
  1223.   jne @@L1
  1224.   cmp [rMargin], LINEWIDTH-1
  1225.   jne @@L1
  1226.   sub ah, ah
  1227.   mov al, [zLMargin]
  1228.   mov [lMargin], ax
  1229.   mov al, [zRMargin]
  1230.   mov [rMargin], ax
  1231.   mov [autoIndent?], 0
  1232.   jmp HomeLine
  1233. @@L1:
  1234.   mov [lMargin], 0
  1235.   mov [rMargin], LINEWIDTH-1
  1236.   mov [autoIndent?], -1
  1237.   jmp HomeLine
  1238.  
  1239. CRet:
  1240. ;Split line at cursor
  1241.   push ds
  1242.   push es
  1243.   push di
  1244.   push es
  1245.   call AddBlankLine             ;Start a new line below current one, ->ES:DI
  1246.   pop ds                        ;DS:SI := current cursor position
  1247.   pop si
  1248.   push di
  1249.   mov cx, LINEWIDTH             ;CX := # chars left on line
  1250.   sub cx, si
  1251.   je @@L2
  1252. @@L1:
  1253.   movsb                         ;Split line,
  1254.   mov [byte si - 1], ' '        ; blank original to end from cursor
  1255.   loop @@L1
  1256. @@L2:
  1257.   pop di
  1258.   pop es
  1259.   pop ds
  1260.   jmp NewLineC
  1261.  
  1262. AddBlankLine:
  1263. ;Insert a new blank line below current one
  1264.   mov cx, 1                     ;Make room for new entry in linePtr
  1265.   call OpenRow
  1266.   inc bx
  1267.   inc bx
  1268.   mov ax, [heapPtr]
  1269.   jmp SHORT BlankLine
  1270.  
  1271. NewFile:
  1272. ;Set up initial blank line of a new file
  1273.   mov ax, [heapPtr]             ;Set ES and [bx] to available heap
  1274.   mov bx, OFFSET linePtrs
  1275.   mov [lastLine], bx
  1276. BlankLine:
  1277.   mov [bx], ax
  1278.   mov es, ax
  1279.   add ax, 5
  1280.   mov [heapPtr], ax             ;Update heap pointer (segment value only)
  1281. BlankThisLine:
  1282.   sub di, di                    ;Blank new line
  1283.   mov cx, LINEWIDTH
  1284.   mov al, ' '
  1285.   rep stosb
  1286.   mov di, [lMargin]             ;Home cursor on new line
  1287.   test [autoIndent?], -1        ; or if in autoindent mode,
  1288.   je @@Lx
  1289.   cmp bx, OFFSET linePtrs       ; and this is not first line in file,
  1290.   je @@Lx
  1291.   mov es, [bx - 2]              ; line up with first char of line above
  1292.   call RScanE
  1293.   mov es, [bx]
  1294.   je @@L1                       ; unless above line is blank
  1295.   dec di
  1296.   cmp di, [lMargin]             ; or first char is left of L margin
  1297.   jae @@Lx
  1298. @@L1:
  1299.   mov di, [lMargin]             ; in which case use L margin
  1300. @@Lx:
  1301.   ret
  1302.  
  1303. OpenRow:
  1304. ;Open CX lines at BX in linePtrs
  1305.   push cx
  1306.   push di
  1307.   push es
  1308.   mov ax, ds                    ;DS, ES -> data segment (for linePtr)
  1309.   mov es, ax
  1310.   mov si, [lastLine]            ;SI points to last line's segment pointer
  1311.   mov di, si                    ;DI points CX lines beyond that
  1312.   add di, cx
  1313.   add di, cx
  1314.   mov [lastLine], di            ;Point lastLine to new last line
  1315.   mov cx, si                    ;Count = # lines from here to end
  1316.   sub cx, bx
  1317.   shr cx, 1
  1318.   inc cx
  1319.   std
  1320.   rep movsw                     ;Move array elements up
  1321.   cld
  1322.   pop es
  1323.   pop di
  1324.   pop cx
  1325.   ret
  1326.  
  1327. BackSpace:
  1328. ;Delete char to left of cursor
  1329.   mov ax, di                    ;Unless at first character of file,
  1330.   add ax, bx
  1331.   sub ax, OFFSET linePtrs
  1332.   jz Ret1                       ; do Left then Delete
  1333.   mov [justFound?], 0
  1334.   push di
  1335.   call Left
  1336.   pop ax                        ;Don't do Join if already at end of line ...
  1337.   or ax, ax
  1338.   jne Delete0
  1339.  
  1340. Delete:
  1341. ;Delete char at cursor
  1342.   mov dx, di                    ;Save cursor column
  1343.   cmp [BYTE es:di], ' '         ;If deleting a space at end of line,
  1344.   jne Delete0
  1345.   call RScanE
  1346.   mov di, dx                    ; join line below
  1347.   je Join
  1348. Delete0:
  1349.   mov [changed?], -1
  1350.   push di                       ; else slide text left
  1351.   push cx
  1352.   push ds
  1353.   mov cx, LINEWIDTH - 1
  1354.   sub cx, di
  1355.   mov si, di
  1356.   inc si
  1357.   mov ax, es
  1358.   mov ds, ax
  1359.   rep movsb
  1360.   mov [BYTE di], ' '            ;Blank last character on line
  1361.   pop ds
  1362.   pop cx
  1363.   pop di
  1364. Ret1:
  1365.   ret
  1366.  
  1367. UndeleteLine:
  1368.   mov bp, [blockPtrsLast]       ;Abort if no lines are in buffer
  1369.   cmp bp, OFFSET blockPtrs
  1370.   ja @@L0
  1371.   jmp Beep
  1372. @@L0:
  1373.   dec bp                        ;Else move pointer to top line of delete buffer
  1374.   dec bp
  1375.   mov [blockPtrsLast], bp
  1376.   cmp di, [lMargin]             ;If cursor is at or before L margin,
  1377.   ja @@L1
  1378.   mov cx, 1
  1379.   call OpenRow                  ;Start new row below current one
  1380.   mov [bx+2], es                ;Swap rows to insert undeleted above current
  1381.   mov ax, [bp]                  ;Retrieve and store pointer to undeleted line
  1382.   mov [bx], ax
  1383.   jmp NewLine
  1384. @@L1:
  1385.   mov cx, LINEWIDTH             ;Cursor past start of line
  1386.   sub cx, di                    ;Copy popped line over current one
  1387.   push di
  1388.   push ds
  1389.   mov ds, [bp]
  1390.   sub si, si
  1391.   rep movsb
  1392.   pop ds
  1393.   pop di
  1394.   ret
  1395.  
  1396. Join:
  1397. ;Join lower line to current line at cursor
  1398.   cmp bx, [lastLine]            ;Abort if this is the last line of the file
  1399.   jne @@L1
  1400.   jmp Beep
  1401. @@L1:
  1402.   push bx                       ;Save registers
  1403.   push di
  1404.   push ds
  1405.   push di
  1406.   push es
  1407.   mov es, [bx + 2]              ;Get next line's segment
  1408.   push es                       ;Save a copy
  1409.   mov dx, di
  1410.   sub di, di                    ;Remove leading spaces
  1411.   call RScanE
  1412.   jne @@L2
  1413.   mov di, 1
  1414. @@L2:
  1415.   dec di
  1416.   mov [temp], di
  1417.   call EndLine                  ;Find first non-space char from end
  1418.   add dx, di                    ;If concatenated line is too long, abort.
  1419.   sub dx, [temp]
  1420.   cmp dx, LINEWIDTH
  1421.   jle @@L3
  1422.   call Beep
  1423.   pop ax
  1424.   pop es
  1425.   pop di
  1426.   pop ds
  1427.   pop ax
  1428.   pop ax
  1429.   jmp Ret1
  1430. @@L3:
  1431.   mov cx, di                    ;Count = lower line length minus leading spaces
  1432.   sub cx, [temp]
  1433.   mov si, [temp]                ;Source = start of text on lower line
  1434.   pop ds
  1435.   pop es                        ;Destination = present cursor location
  1436.   pop di
  1437.   rep movsb                     ;Concatenate lines
  1438.   pop ds
  1439.   inc bx                        ;Delete lower line
  1440.   inc bx
  1441.   call DeleteLineNS
  1442.   pop di                        ;Restore pointers and return
  1443.   pop bx
  1444.   jmp NewLineC
  1445.  
  1446. Insert:
  1447. ;Insert or overwrite at cursor
  1448.   mov [justFound?], 0
  1449.   test [inserting?], -1         ;If inserting, open up space for new character
  1450.   jz Insert1
  1451.   mov si, [RMargin]             ;If line is full, split it
  1452.   cmp [BYTE es:si], ' '
  1453.   je Insert0
  1454.   push ax
  1455.   push bx
  1456.   push di
  1457.   call CRet
  1458.   pop di
  1459.   pop bx
  1460.   call NewLine
  1461.   pop ax
  1462.   jmp SHORT Insert1
  1463. Insert0:
  1464.   push ax
  1465.   push cx
  1466.   push ds
  1467.   mov ax, es
  1468.   mov ds, ax
  1469.   mov si, LINEWIDTH - 1
  1470.   mov cx, si
  1471.   sub cx, di
  1472.   mov di, si
  1473.   dec si
  1474.   std
  1475.   rep movsb
  1476.   cld
  1477.   pop ds
  1478.   pop cx
  1479.   pop ax
  1480. Insert1:
  1481.   stosb                         ;Add character
  1482.   mov [changed?], -1
  1483.   cmp di, [rMargin]             ;Wrap if at R margin
  1484.   ja WrapLine
  1485.   ret
  1486.  
  1487. WrapLine:
  1488. ;Wrap last word on current line
  1489.   cmp [BYTE es:di-1], ' '
  1490.   je @@L1
  1491.   call WordLeft
  1492. @@L1:
  1493.   call CRet
  1494.   mov es, [bx]
  1495.   cmp [BYTE es:di], ' '
  1496.   jne @@L2
  1497.   jmp DeleteWordR
  1498. @@L2:
  1499.   jmp EndLine
  1500.  
  1501. Wrap:
  1502. ;Wrap paragraph starting at present line
  1503.   mov di, [rMargin]             ;Start at R margin of present line
  1504.   cmp bx, [lastLine]            ;Quit if we're at end of file
  1505.   je @@Lx
  1506. @@L1:
  1507.   mov dx, di
  1508.   call RScanE                   ;Is there text between here and end of line?
  1509.   cmp di, LINEWIDTH
  1510.   mov di, dx
  1511.   je @@L2
  1512.   call LScanNE                  ;Yes.  Is there a space at which to split line?
  1513.   or di, di
  1514.   mov di, dx
  1515.   jle @@Lx                      ;No, stop wrapping
  1516.   call FindBreak                ;Yes, split this line, drop to lower line
  1517.   call CRet
  1518.   jmp Wrap
  1519. @@L2:
  1520.   call EndLine                  ;Put cursor after end of text on line.
  1521.   mov al, [es:di-1]             ;If last char is in {.;:!?}, add two spaces
  1522.   cmp al, 40h
  1523.   jae @@L2b
  1524.   cmp al, '.'
  1525.   je @@L2a
  1526.   cmp al, ';'
  1527.   je @@L2a
  1528.   cmp al, ':'
  1529.   je @@L2a
  1530.   cmp al, '!'
  1531.   je @@L2a
  1532.   cmp al, '?'
  1533.   jne @@L2b
  1534. @@L2a:
  1535.   inc di
  1536. @@L2b:
  1537.   inc di                        ;After other chars, just add one space
  1538.   mov dx, [rMargin]             ;Calculate spaces remaining on upper line ->DX
  1539.   sub dx, di
  1540.   ja @@L3                       ;If no room for more text, next line
  1541.   call Down
  1542.   call KeepWrapping?
  1543.   jnc @@Lx
  1544.   jmp Wrap
  1545. @@L3:
  1546.   mov [topSegPtr], bx           ;Save pointers for this line
  1547.   mov [topSeg], es
  1548.   mov [topIndex], di
  1549.   call Down                     ;Drop down to next line
  1550.   call KeepWrapping?            ;Does text start at L margin?
  1551.   jc @@L4
  1552. @@Lx:
  1553.   jmp NewLine                   ;No, we're done
  1554. @@L4:
  1555.   add di, dx                    ;Yes, move words up from lower line
  1556.   call FindBreak                ;Find right place to break line
  1557.   mov dx, di                    ;Set up pointers and count for move
  1558.   mov si, [lMargin]
  1559.   sub bp, si
  1560.   jnz @@L4a                     ;If nothing will fit, next line
  1561.   jmp Wrap
  1562. @@L4a:
  1563.   mov ax, es
  1564.   mov di, [topIndex]
  1565.   mov es, [topSeg]
  1566.   mov bx, [topSegPtr]
  1567.   mov ds, ax
  1568.   mov cx, bp
  1569.   rep movsb                     ;Move part of lower line up
  1570.   mov cx, cs
  1571.   mov ds, cx
  1572.   cmp dx, LINEWIDTH - 1         ;If nothing left on lower line,
  1573.   jne @@L5
  1574.   call Down
  1575.   call DeleteLine               ;Delete it
  1576.   cmp bx, [lastLine]            ;Done if that was last line
  1577.   je @@Lx
  1578.   mov es, [topSeg]              ;Get top line back and keep wrapping
  1579.   mov bx, [topSegPtr]
  1580.   jmp Wrap
  1581. @@L5:
  1582.   call Down                     ;Else slide lower line left
  1583.   mov si, dx
  1584.   mov di, [lMargin]
  1585.   call CloseGap
  1586.   jmp Wrap                      ;Continue wrapping with lower line
  1587.  
  1588. KeepWrapping?:
  1589. ;Set DI to L margin.  Return C = 1 if line <- BX has text starting at L margin.
  1590.   mov di, [lMargin]
  1591.   cmp [BYTE es:di], ' '         ;Fail if space at margin
  1592.   je @@Lx
  1593.   or di, di
  1594.   je @@L1
  1595.   push di
  1596.   dec di                        ; or chars before margin
  1597.   call LScanE
  1598.   pop di
  1599.   jne @@Lx
  1600. @@L1:
  1601.   stc                           ; else return C = 1
  1602.   db 3Ch                        ;'cmp al, ?' trick to skip one byte
  1603. @@Lx:
  1604.   clc
  1605.   ret
  1606.  
  1607. FindBreak:
  1608. ;Find place to break line while wrapping.  Return BP = last char to move.
  1609.   cmp [BYTE es:di], ' '
  1610.   je @@L1
  1611.   inc di
  1612.   call WordLeft
  1613.   mov bp, di
  1614.   ret
  1615. @@L1:
  1616.   mov bp, di
  1617.   jmp WordRight
  1618.  
  1619. DeleteToEOL:
  1620. ;Delete from cursor to end of line
  1621.   mov [justFound?], 0
  1622.   push bx                       ;Save regs to return to current cursor position
  1623.   push di
  1624.   push es
  1625.   push [word autoIndent?]       ;Turn autoIndent off
  1626.   mov [autoIndent?], 0
  1627.   call Cret                     ;Do Enter then delete lower line
  1628.   call DeleteWordL
  1629.   call DeleteLine
  1630.   pop [word autoIndent?]
  1631.   pop es
  1632.   pop di
  1633.   pop bx
  1634.   jmp NewLine
  1635.  
  1636. DeleteLine:
  1637. ;Delete cursor line and append to buffer
  1638.   mov [changed?], -1
  1639.   cmp bx, [lastLine]            ;If last line of file, just blank it
  1640.   jne @@L1
  1641.   jmp BlankThisLine
  1642. @@L1:
  1643.   mov bp, [blockPtrsLast]       ;Save segment of current line in delete buffer
  1644.   mov [bp], es
  1645.   inc bp
  1646.   inc bp
  1647.   mov [blockPtrsLast], bp
  1648. DeleteLineNS:                   ;Enter here if we don't want to save line
  1649.   mov di, bx                    ;Delete line:  destination = this line
  1650.   mov si, di                    ;Source = next line
  1651.   inc si
  1652.   inc si
  1653.   mov cx, [lastLine]            ;Count = number of lines from here to end
  1654.   mov ax, cx
  1655.   dec ax
  1656.   dec ax
  1657.   mov [lastLine], ax
  1658.   sub cx, bx
  1659.   shr cx, 1
  1660.   mov ax, ds                    ;Move line segment values above cursor down
  1661.   mov es, ax
  1662.   rep movsw
  1663.   mov di, [lMargin]             ;Home cursor on new line
  1664.   jmp NewLine
  1665.  
  1666. DeleteWordL:
  1667. ;Delete left to space or column zero
  1668.   mov ah, 2                     ;Check for Ctrl key down.  ^[ or ESC?
  1669.   int 16h                       ;If ^[, delete word left.  If ESC, quit.
  1670.   and al, 4
  1671.   je DWLx
  1672.   mov si, di                    ;Save cursor column
  1673.   call WordLeft                 ;Tab left one word
  1674. CloseGap:
  1675.   push di                       ;Close gap between di and si cursor positions
  1676.   mov cx, LINEWIDTH
  1677.   sub cx, si
  1678.   push ds
  1679.   mov ax, es
  1680.   mov ds, ax
  1681.   rep movsb
  1682.   mov cx, LINEWIDTH             ;Pad end of line with spaces
  1683.   sub cx, di
  1684.   mov al, ' '
  1685.   rep stosb
  1686.   pop ds
  1687.   pop di
  1688.   mov [changed?], -1
  1689.   ret
  1690. DWLx:
  1691.   jmp Exit
  1692.  
  1693. DeleteWordR:
  1694. ;Delete right to space or end of line
  1695.   mov si, di                    ;Save cursor
  1696.   push di
  1697.   call WordRight                ;Tab right one word
  1698.   xchg si, di                   ;Close up space between si and di
  1699.   pop di
  1700.   jmp CloseGap
  1701.  
  1702. ToggleIns:
  1703.   not [inserting?]
  1704.   ret
  1705.  
  1706. Jump:
  1707. ;Jump to line number n
  1708.   mov si, OFFSET gotoMsg
  1709.   call Prompt
  1710.   call GetInt
  1711.   dec ax
  1712.   shl ax, 1
  1713.   mov bx, ax
  1714.   add bx, OFFSET linePtrs
  1715.   mov [justFound?], 0
  1716.   jmp SHORT JL1                 ;Jump to address
  1717.  
  1718. GetInt:
  1719. ;Get a decimal integer from keyboard to AX.  Carry set on improper input.
  1720. ;Abort if null input.
  1721.   push bx
  1722.   push cx
  1723.   push dx
  1724.   push si
  1725.   mov dx, OFFSET buffer
  1726.   call GetString                ;Input a string
  1727.   mov cx, ax                    ;Construct integer a digit at a time
  1728.   mov si, OFFSET buffer
  1729.   sub ax, ax
  1730.   mov bh, 10
  1731. @@L1:
  1732.   mov bl, [si]                  ;Get next char
  1733.   inc si
  1734.   sub bl, '0'
  1735.   jc @@Lx                       ;Exit with carry set if not a digit
  1736.   cmp bl, '9'
  1737.   cmc
  1738.   jc @@Lx
  1739.   mul bh                        ;AX := AX + (new digit - '0')
  1740.   add al, bl
  1741.   adc ah, 0
  1742.   jc @@Lx                       ;Check for overflow
  1743.   loop @@L1                     ;Next char
  1744. @@Lx:
  1745.   pop si                        ;Return with int in AX, carry set if error
  1746.   pop dx
  1747.   pop cx
  1748.   pop bx
  1749.   ret
  1750. @@La:
  1751.   mov si, OFFSET cancelledMsg
  1752.   jmp Abort
  1753.  
  1754. SetLabel:
  1755. ;Set label 0-9 at current line
  1756.   call GetLabel
  1757.   mov [si], bx
  1758.   ret
  1759.  
  1760. GotoLabel:
  1761. ;Goto label 0-9 previously set by SetLabel
  1762.   call GetLabel
  1763.   cmp [WORD si], 0              ;Cancel if label not assigned
  1764.   je JLx
  1765.   mov bx, [si]                  ;Retrieve address
  1766.   mov [justFound?], 0
  1767. JL1:
  1768.   mov ax, bx
  1769.   sub ax, 8                     ;Make cursor line fifth from top
  1770.   cmp ax, OFFSET linePtrs
  1771.   jge @@L1
  1772.   mov ax, OFFSET linePtrs
  1773. @@L1:
  1774.   mov [top], ax
  1775. JLx:
  1776.   jmp NewLine0
  1777.  
  1778. GetLabel:
  1779.   mov si, OFFSET setLabelMsg
  1780.   call Prompt
  1781.   mov ah, 8                     ;Get char from keyboard
  1782.   int 21h
  1783.   mov dl, al                    ;Save copy to echo
  1784.   sub al, '0'                   ;Don't accept input if not a digit
  1785.   jl GetLabel
  1786.   cmp al, 9
  1787.   jg GetLabel
  1788.   mov ah, 2
  1789.   mov cl, al
  1790.   int 21h
  1791.   mov si, OFFSET LabelTable     ;Form index into LabelTable
  1792.   shl cl, 1
  1793.   sub ch, ch
  1794.   add si, cx                    ;Return address of label storage in SI
  1795.   ret
  1796.  
  1797. SetTabs:
  1798. ;Set tab width
  1799.   mov si, OFFSET setTabsMsg
  1800.   call Prompt
  1801.   call GetInt
  1802.   mov [tabSize], al
  1803.   ret
  1804.  
  1805. AutoIndent:
  1806. ;Toggle autoindent mode
  1807.   not [autoIndent?]
  1808.   ret
  1809.  
  1810. Kill:
  1811. ;Toggle changed? flag for file changes to be discarded on exit
  1812.   not [changed?]
  1813.   ret
  1814.  
  1815. Save:
  1816. ;Write lines to file, renaming old version with .BAK extension
  1817.   cmp [changed?], 0             ;If no changes, done.
  1818.   je XSave
  1819.   push dx
  1820.   push di
  1821.   push es
  1822.   push bx
  1823.   mov al, [newFile?]            ;If a new file, create it first
  1824.   or al, [isBAKed?]             ;If already BAKed up, no .BAK needed
  1825.   jnz DoSave
  1826.   mov ax, ds                    ;Else make new ASCIIZ str with .BAK extension
  1827.   mov es, ax
  1828.   mov si, OFFSET fName
  1829.   mov di, OFFSET fNameBAK
  1830. @@L1:
  1831.   lodsb
  1832.   cmp al, '.'
  1833.   je @@L2
  1834.   or al, al
  1835.   je @@L2
  1836.   stosb
  1837.   jmp SHORT @@L1
  1838. @@L2:
  1839.   mov cx, 5
  1840.   mov si, OFFSET BAK
  1841.   rep movsb
  1842.   mov ah, 41h                   ;Delete old back-up copy if present
  1843.   mov dx, OFFSET fNameBAK
  1844.   int 21h
  1845.   mov dx, OFFSET fName          ;Rename current file to file.BAK
  1846.   mov di, OFFSET fNameBAK
  1847.   mov ah, 56h
  1848.   int 21h
  1849. DoSave:
  1850.   mov ah, 3Ch                   ;CREATe new file with old name
  1851.   sub cx, cx
  1852.   mov dx, OFFSET fName
  1853.   int 21h
  1854.   jc CantOpen
  1855.   mov [fHandle], ax
  1856.   mov [isBAKed?], -1            ;Set flag so we only make .BAK file once
  1857.   mov bx, OFFSET linePtrs       ;Write file
  1858.   call WriteFile
  1859.   mov ah, 3Eh                   ;Close file
  1860.   mov bx, [fHandle]
  1861.   int 21h
  1862.   pop bx
  1863.   pop es
  1864.   pop di
  1865.   pop dx
  1866.   mov [changed?], 0
  1867. XSave:
  1868.   ret
  1869.  
  1870. WriteFile:
  1871. ;Write lines out to file [fHandle] starting at BX and ending at [lastLine]
  1872.   push es
  1873.   push di
  1874.   mov di, OFFSET buffer
  1875. @@L1:
  1876.   mov si, di                    ;Preserve file buffer pointer
  1877.   mov es, [bx]                  ;Strip trailing blanks
  1878.   mov cx, LINEWIDTH
  1879.   mov di, LINEWIDTH - 1
  1880.   mov al, ' '
  1881.   std
  1882.   repe scasb
  1883.   cld
  1884.   je @@L1a
  1885.   inc cx
  1886. @@L1a:
  1887.   mov ax, ds                    ;Copy line to file buffer
  1888.   mov dx, es
  1889.   mov es, ax
  1890.   mov ds, dx
  1891.   mov di, si
  1892.   sub si, si
  1893.   rep movsb
  1894.   mov ax, CRLF                  ;Stick a CRLF on the end
  1895.   stosw
  1896.   mov ax, ds
  1897.   mov dx, es
  1898.   mov es, ax
  1899.   mov ds, dx
  1900.   cmp di, OFFSET Buffer + BUFFERLENGTH - 80  ;If buffer is almost full,
  1901.   jl @@L2
  1902.   call WriteBuffer              ; write it
  1903. @@L2:
  1904.   inc bx                        ;Next line, loop until all lines are written
  1905.   inc bx
  1906.   cmp bx, [lastLine]
  1907.   jle @@L1
  1908.   dec di                        ;Delete last CR, LF
  1909.   dec di
  1910.   call WriteBuffer              ;Write final partial buffer to file and exit
  1911.   pop di
  1912.   pop es
  1913.   ret
  1914.  
  1915. FileError:
  1916.   mov si, OFFSET fileErrorMsg
  1917.   jmp Abort
  1918. CantOpen:
  1919.   mov si, OFFSET cantOpenMsg
  1920.   jmp Abort
  1921. NoRoom:
  1922.   mov si, OFFSET noRoomMsg
  1923.   jmp Abort
  1924. ReadError:
  1925.   mov si, OFFSET rdErrorMsg
  1926.   jmp Abort
  1927. DiskFull:
  1928.   mov dx, OFFSET fName          ;Attempt to delete file first
  1929.   mov ah, 41h
  1930.   int 21h
  1931.   mov si, OFFSET diskFullMsg
  1932.   jmp Abort
  1933.  
  1934. WriteBuffer:
  1935. ;Write text in buffer to disk
  1936.   push bx
  1937.   mov bx, [fHandle]
  1938.   mov cx, di
  1939.   mov dx, OFFSET Buffer
  1940.   sub cx, dx
  1941.   jle @@L1
  1942.   push cx
  1943.   mov ah, 40h
  1944.   int 21h
  1945.   jc FileError
  1946.   pop cx
  1947.   cmp cx, ax
  1948.   jne DiskFull
  1949.   mov di, OFFSET Buffer
  1950. @@L1:
  1951.   pop bx
  1952.   ret
  1953.  
  1954. Exit:
  1955.   call Save                     ;Save file if changed
  1956.   call RestoreCursor
  1957.   mov ax, 4C00h                 ;Bye!
  1958.   int 21h
  1959.  
  1960. RestoreCursor:
  1961.   mov cx, [cursorShape]         ;Restore standard cursor size
  1962.   mov ah, 1
  1963.   int 10h
  1964.   mov dx, 1800h                 ;Put cursor at bottom of screen
  1965.   sub bh, bh
  1966.   mov ah, 2
  1967.   int 10h
  1968.   ret
  1969.  
  1970. BeginBlock:
  1971. ;Start marking block for block operation
  1972.   mov [marking?], -1
  1973.   mov [mark], bx
  1974.   ret
  1975.  
  1976. Unmark:
  1977. ;Clear marking
  1978.   mov [marking?], 0
  1979.   ret
  1980.  
  1981. InsertBlock:
  1982. ;Insert from buffer or named file
  1983.   push bx
  1984.   call FileOrBuffer?            ;From file or buffer?
  1985.   je InsertBuffer
  1986.   mov dx, OFFSET fNameTemp      ;If file, open it
  1987.   mov ax, 3D00h
  1988.   int 21h
  1989.   jnc @@L1
  1990.   jmp CantOpen
  1991. @@L1:
  1992.   mov dx, ax                    ;Load file
  1993.   mov bx, OFFSET blockPtrs
  1994.   mov di, bx
  1995.   call ReadFile
  1996.   mov cx, bx
  1997.   mov bx, dx                    ;Close file
  1998.   mov ah, 3Eh
  1999.   int 21h
  2000.   jmp SHORT DoInsert
  2001. InsertBuffer:                   ;Insert from buffer
  2002.   mov bp, [blockPtrsLast]       ;Abort if empty
  2003.   cmp bp, OFFSET blockPtrs
  2004.   jne @@L0
  2005.   pop bx
  2006.   jmp na
  2007. @@L0:
  2008.   test [needCopies?], -1        ;If not just moving lines, need to duplicate
  2009.   jz @@L2
  2010.   push bx
  2011.   push es
  2012.   push ds
  2013.   mov bx, bp
  2014.   mov dx, [heapPtr]
  2015. @@L1:
  2016.   dec bx                        ;Copy contents of buffered lines to new ones
  2017.   dec bx
  2018.   mov ds, [cs:bx]
  2019.   sub si, si
  2020.   mov es, dx
  2021.   sub di, di
  2022.   mov [cs:bx], es
  2023.   mov cx, LINEWIDTH
  2024.   rep movsb
  2025.   add dx, 5
  2026.   cmp dx, 0A000h
  2027.   jb @@L1a
  2028.   jmp NoRoom
  2029. @@L1a:
  2030.   cmp bx, OFFSET blockPtrs
  2031.   ja @@L1
  2032.   pop ds
  2033.   pop es
  2034.   pop bx
  2035.   mov [heapPtr], dx
  2036. @@L2:
  2037.   mov [needCopies?], -1
  2038.   mov cx, bp
  2039. DoInsert:
  2040.   pop bx
  2041.   sub cx, OFFSET blockPtrs      ;Get count of lines to move
  2042.   shr cx, 1
  2043.   call OpenRow                  ;Open that much room in array of seg pointers
  2044.   mov si, OFFSET blockPtrs      ;Copy new lines into opening
  2045.   mov di, bx
  2046.   mov ax, ds
  2047.   mov es, ax
  2048.   rep movsw
  2049.   sub di, di
  2050.   jmp NewLineC
  2051.  
  2052. FileOrBuffer?:
  2053. ;Prompt for file name if Shift is down, put it in fNameTemp.
  2054. ;If return with Z set, Shift was up, indicating buffer is to be used.
  2055.   call Shifted?                 ;If shift is down, prompt for file name
  2056.   jz Ret4
  2057.   mov dx, OFFSET fNameTemp
  2058.   call GetFileName
  2059.   jnz Ret4                      ;If null string returned, abort
  2060.   mov si, OFFSET cancelledMsg
  2061.   jmp Abort
  2062.  
  2063. Shifted?:
  2064. ;Get shift key status, returned in AL.  Z set if no shift.
  2065.   mov ah, 2
  2066.   int 16h
  2067.   and al, 3
  2068. Ret4:
  2069.   ret
  2070.  
  2071. EmptyBuffer:
  2072. ;If Shifted, write block buffer to file, else discard
  2073.   mov bp, [blockPtrsLast]       ;Abort if buffer is empty
  2074.   cmp bp, OFFSET blockPtrs
  2075.   jne @@L0
  2076.   jmp Beep
  2077. @@L0:
  2078.   call Shifted?                 ;If shifted, write to file
  2079.   je @@L1
  2080.   push bx
  2081.   mov dx, OFFSET fNameTemp
  2082.   call GetFileName
  2083.   mov si, OFFSET blockPtrs
  2084.   mov bx, bp
  2085.   dec bx
  2086.   dec bx
  2087.   call WriteBlock
  2088.   pop bx
  2089. @@L1:
  2090.   mov ax, OFFSET blockPtrs      ;Else just reset buffer pointer
  2091.   mov [blockPtrsLast], ax
  2092. @@Lx:
  2093.   ret
  2094.  
  2095. WriteBlock:
  2096. ;Write block to file.  SI -> starting seg pointer, BX -> ending seg pointer.
  2097.   push [lastLine]               ;Copy block buffered lines to file:
  2098.   push [fHandle]
  2099.   mov ah, 3Ch                   ;CREATe file
  2100.   sub cx, cx
  2101.   mov dx, OFFSET fNameTemp
  2102.   int 21h
  2103.   jnc @@L1
  2104.   jmp CantOpen
  2105. @@L1:
  2106.   mov [fHandle], ax             ;Write it
  2107.   mov [lastLine], bx
  2108.   mov bx, si
  2109.   call WriteFile
  2110.   mov bx, [fHandle]             ;Close it
  2111.   mov ah, 3Eh
  2112.   int 21h
  2113.   pop [fHandle]
  2114.   pop [lastLine]
  2115.   ret
  2116.  
  2117. Copy:
  2118. ;Copy marked lines, to file if shift is down, otherwise to buffer.
  2119.   test [marking?], -1          ;Abort with a beep if not already marking,
  2120.   jnz @@L1
  2121.   mov si, OFFSET notMarkingMsg
  2122.   jmp Abort
  2123. @@L1:
  2124.   mov si, [mark]
  2125.   cmp bx, si                    ;If mark comes after here, exchange
  2126.   jae @@L2
  2127.   xchg bx, si
  2128.   mov [mark], si                ; save in this order for possible delete
  2129. @@L2:
  2130.   push bx
  2131.   push di
  2132.   push es
  2133.   call FileOrBuffer?            ;If Shift key was down when command entered,
  2134.   je @@L4
  2135.   call WriteBlock
  2136.   mov di, OFFSET blockPtrs
  2137.   jmp SHORT @@Lx
  2138. @@L4:
  2139.   mov cx, bx                    ;If no Shift, move marked lines to buffer
  2140.   sub cx, si
  2141.   shr cx, 1
  2142.   inc cx
  2143.   mov di, OFFSET blockPtrs
  2144.   mov ax, ds
  2145.   mov es, ax
  2146.   rep movsw
  2147. @@Lx:
  2148.   mov [blockPtrsLast], di       ;Save pointer to last line
  2149.   pop es
  2150.   pop di
  2151.   pop bx
  2152.   mov [marking?], 0
  2153.   ret
  2154.  
  2155. DeleteBlock:
  2156. ;Do a Copy, then delete copied lines
  2157.   call Copy                     ;Copy block to file or buffer
  2158.   mov si, bx                    ;Close up copied lines:
  2159.   inc si                        ;SI = cursor line + 2
  2160.   inc si
  2161.   mov di, [mark]                ;DI = start of marking
  2162.   mov cx, [lastLine]            ;CX = number of lines from here to end
  2163.   sub cx, bx
  2164.   push es
  2165.   mov ax, ds
  2166.   mov es, ax
  2167.   rep movsb
  2168.   pop es
  2169.   dec di
  2170.   dec di
  2171.   cmp di, OFFSET linePtrs       ;If whole file deleted, start new file
  2172.   jae @@L1
  2173.   call NewFile
  2174.   jmp NewLineC
  2175. @@L1:
  2176.   mov [lastLine], di            ;Else store index of new last line
  2177.   mov di, [lMargin]             ;Point cursor to start of old marked line
  2178.   mov bx, [mark]
  2179.   mov [needCopies?], 0
  2180.   jmp NewLineC
  2181.  
  2182. Find:
  2183. ;Prompt for and find next <string>.  If Shifted, reuse last <string>.
  2184.   push bx
  2185.   push es
  2186.   push di
  2187.   test [autoReplace?], -1       ;If doing replace all, bypass shift check
  2188.   js @@L1
  2189.   jnz @@L4
  2190.   call Shifted?                 ;If Shifted,
  2191.   jnz @@L4
  2192. @@L1:
  2193.   mov si, OFFSET findMsg        ;Get search string
  2194.   mov dx, OFFSET findString
  2195.   call GetCountedString
  2196.   sub ah, ah
  2197.   mov cx, ax
  2198.   mov si, OFFSET findString+1   ;Convert to all upper case
  2199. @@L2:
  2200.   lodsb
  2201.   cmp al, 'a'
  2202.   jb @@L3
  2203.   cmp al, 'z'
  2204.   ja @@L3
  2205.   add al, 'A' - 'a'
  2206.   mov [si-1], al
  2207. @@L3:
  2208.   loop @@L2
  2209. @@L4:
  2210.   mov dl, [byte findString]     ;Save copy of string length
  2211.   mov cx, LINEWIDTH-2
  2212.   sub cx, di
  2213.   inc di
  2214.   mov si, di
  2215. FindLoop:
  2216.   mov di, OFFSET findString+1   ;Get pointer to find string
  2217.   mov ah, [di]                  ;AH = first char of string to find
  2218. @@L1:
  2219.   seges                         ;Quick match for first char
  2220.   lodsb
  2221.   xor al, ah
  2222.   and al, 11011111b             ;(case-insensitive)
  2223.   loopne @@L1
  2224.   jcxz FindNextLine
  2225.   mov [temp], si                ;First char matched, save place, check rest
  2226.   mov [SPTemp], cx
  2227.   dec si
  2228.   mov dh, dl
  2229. @@L2:
  2230.   seges                         ;Get a char of find string
  2231.   lodsb
  2232.   mov ah, [di]                  ;Convert text char to upper case
  2233.   inc di
  2234.   cmp al, 'a'
  2235.   jb @@L3
  2236.   cmp al, 'z'
  2237.   ja @@L3
  2238.   add al, 'A' - 'a'
  2239. @@L3:
  2240.   cmp al, ah                    ;Compare
  2241.   jne @@L4
  2242.   dec dh
  2243.   loopnz @@L2
  2244.   jnz FindNextLine
  2245.   mov di, si                    ;Match. Point cursor at start of found string.
  2246.   sub di, dx
  2247.   add sp, 6                     ;Discard old line index, segment
  2248.   mov [justFound?], -1
  2249.   mov ax, bx                    ;Show found line 5 below top of screen
  2250.   jmp JL1
  2251. @@L4:
  2252.   mov si, [temp]                ;Match failed.  Keep looking.
  2253.   mov cx, [SPTemp]
  2254.   jmp FindLoop
  2255. FindNextLine:
  2256.   sub si, si                    ;Search next line (until EOF)
  2257.   mov cl, LINEWIDTH-1
  2258.   inc bx
  2259.   inc bx
  2260.   mov es, [bx]
  2261.   cmp bx, [lastLine]
  2262.   jbe FindLoop
  2263.   mov si, OFFSET notFoundMsg    ;Not found
  2264.   test [autoReplace?], -1       ;If doing auto-replace, return
  2265.   jz @@L1
  2266.   mov [autoReplace?], 0
  2267.   pop di
  2268.   pop es
  2269.   pop bx
  2270.   ret
  2271. @@L1:
  2272.   call Print0                   ;Else restore cursor, abort with error message
  2273.   pop di
  2274.   pop es
  2275.   pop bx
  2276.   jmp na
  2277.  
  2278. GetCountedString:
  2279. ;Print prompt string at [SI], read counted string into [DX].
  2280. ;Returns count (minus CR/LF) in AL.  DX is advanced one to start of string.
  2281.   push di
  2282.   push dx
  2283.   call Prompt                   ;Display prompt
  2284.   pop dx
  2285.   mov di, dx
  2286.   inc dx
  2287.   call GetString                ;Get input string
  2288.   mov [di], al                  ;Store count in front of string
  2289.   pop di
  2290.   ret
  2291.  
  2292. Replace:
  2293.   push di
  2294.   test [autoReplace?], -1       ;If using auto-replace command,
  2295.   jz @@L0
  2296.   jns @@L2                      ;Skip shift check
  2297.   mov [autoReplace?], 1
  2298.   jmp SHORT @@L1a               ;Get replace string if shifted and first pass
  2299. @@L0:
  2300.   test [justFound?], -1         ;Beep if not immediately preceded by a Find
  2301.   jnz @@L1
  2302. @@LE:
  2303.   pop di
  2304.   jmp na
  2305. @@L1:
  2306.   call Shifted?                 ;If not Shifted, prompt for replace string
  2307.   jnz @@L2
  2308. @@L1a:
  2309.   mov si, OFFSET replaceMsg
  2310.   mov dx, OFFSET replaceString
  2311.   call GetCountedString
  2312. @@L2:
  2313.   mov si, OFFSET replaceString
  2314.   lodsb
  2315.   sub ah, ah
  2316.   mov dx, si
  2317.   push ax
  2318.   push dx
  2319.   sub ch, ch                    ;Compare lengths of find and replace strings
  2320.   mov cl, al
  2321.   sub cl, [byte findString]     ;If replace string is longer,
  2322.   je @@L6
  2323.   jb @@L4
  2324.   xchg dx, di
  2325.   call EndLine                  ; make sure there will be enough room on line
  2326.   xchg dx, di
  2327.   add dl, cl
  2328.   cmp dl, LINEWIDTH
  2329.   ja @@LE
  2330. @@L3:
  2331.   call Insert0                  ; then insert extra characters
  2332.   loop @@L3
  2333.   jmp SHORT @@L6
  2334. @@L4:
  2335.   neg cl                        ;If shorter, delete difference
  2336. @@L5:
  2337.   call Delete0
  2338.   loop @@L5
  2339. @@L6:
  2340.   pop si                        ;Now copy new string over old
  2341.   pop cx
  2342.   sub ch, ch
  2343.   pop di
  2344.   rep movsb
  2345.   jmp NewLineC
  2346.  
  2347. ReplaceAll:
  2348. ;Find and replace all.  If shift not down, prompt for strings.
  2349.   push [top]
  2350.   push es
  2351.   push di
  2352.   push bx
  2353.   mov [autoReplace?], 1         ;Set flag: 1 = no shift, -1 = shift
  2354.   call Shifted?
  2355.   jne @@L1
  2356.   mov [autoReplace?], -1
  2357. @@L1:
  2358.   call Find                     ;Find/replace until flag reset by not found
  2359.   test [autoReplace?], -1
  2360.   jz @@Lx
  2361.   call Replace
  2362.   jmp @@L1
  2363. @@Lx:
  2364.   pop bx                        ;Restore original position
  2365.   pop di
  2366.   pop es
  2367.   pop [top]
  2368.   push si
  2369.   call ReDraw                   ;Refresh screen with any changes
  2370.   pop si
  2371.   push di
  2372.   call Print0                   ;Show notFoundMsg and exit
  2373.   pop di
  2374.   jmp NextNoRedraw
  2375.  
  2376. Help:
  2377.   push es
  2378.   push di
  2379.   mov es, [videoSegment]
  2380.   mov di, 160
  2381.   mov si, OFFSET helpMsg
  2382.   mov cx, 80 * 24
  2383.   mov ah, [attribNl]
  2384. @@L1:
  2385.   lodsb
  2386.   stosw
  2387.   loop @@L1
  2388.   mov si, OFFSET anyKeyMsg
  2389.   call Print0
  2390.   sub ah, ah
  2391.   int 16h
  2392.   pop di
  2393.   pop es
  2394.   ret
  2395.  
  2396. F3BAT:
  2397.   mov dl, '3'
  2398.   jmp SHORT FnBAT
  2399.  
  2400. F4BAT:
  2401.   mov dl, '4'
  2402.   jmp SHORT FnBAT
  2403.  
  2404. F5BAT:
  2405.   mov dl, '5'
  2406.   jmp SHORT FnBAT
  2407.  
  2408. F6BAT:
  2409.   mov dl, '6'
  2410.  
  2411. FnBAT:
  2412. ;Execute 'EFn.BAT f'  where n = DL and f = current file name minus extension
  2413.   push es
  2414.   push di
  2415.   mov [EXECFNumber], dl         ;Which .BAT file?
  2416.   mov ax, cs                    ;Copy file name to command line string
  2417.   mov es, ax
  2418.   mov di, OFFSET EXECFileName
  2419.   mov si, OFFSET fName
  2420.   sub cl, cl
  2421. @@L1:
  2422.   lodsb
  2423.   or al, al                     ; up to '.' or null (ie, omit extension)
  2424.   je @@L2
  2425.   cmp al, '.'
  2426.   je @@L2
  2427.   stosb
  2428.   inc cl
  2429.   jmp @@L1
  2430. @@L2:
  2431.   mov al, CR
  2432.   stosb
  2433.   add cl, 11
  2434.   mov [EXECBAT], cl             ;Store count to include file name
  2435.   pop di
  2436.   pop es
  2437.   mov ax, OFFSET EXECBAT
  2438.   jmp SHORT Shell1
  2439.  
  2440. Shell:
  2441. ;Shell to DOS
  2442.   mov ax, OFFSET EXECParams     ;Point to null command line tail
  2443. Shell1:
  2444.   mov [EXECCmdLineOff], ax
  2445.   push bx                       ;Save regs
  2446.   push es
  2447.   push di
  2448.   call Shifted?                 ;Get and save shift status
  2449.   pushf
  2450.   call Save                     ;Save file being edited
  2451.   popf                          ;If Shift was down, release file, buffers
  2452.   jz @@L1
  2453.   mov bx, ((PROGLENGTH + ESSENTIALS) SHR 4) + 1
  2454.   mov [swapped?], -1
  2455.   jmp SHORT @@L2
  2456. @@L1:
  2457.   mov bx, [heapPtr]             ; else keep file and buffers in memory
  2458.   mov ax, cs
  2459.   sub bx, ax
  2460.   mov [swapped?], 0
  2461. @@L2:
  2462.   mov ax, cs                    ;Free all unneeded memory
  2463.   mov es, ax
  2464.   mov ah, 4Ah
  2465.   int 21h
  2466.   jc SysErr
  2467.   call RestoreCursor            ;Restore cursor
  2468.   mov [SPTemp], sp              ;Do EXEC fn
  2469.   mov dx, [comspecPtrOff]
  2470.   mov ds, [comspecPtrSeg]
  2471.   mov bx, OFFSET EXECParams
  2472.   mov ax, 4B00h
  2473.   int 21h
  2474.   mov ax, cs                    ;Reclaim memory
  2475.   mov ds, ax
  2476.   mov ss, ax
  2477.   mov sp, [spTemp]
  2478.   jc SysErr
  2479.   mov ah, 4Ah
  2480.   mov bx, -1
  2481.   int 21h
  2482.   mov ah, 4Ah
  2483.   int 21h
  2484.   jc SysErr
  2485.   cmp [swapped?], 0             ;If edited file was swapped out,
  2486.   je XEXEC
  2487.   mov dx, OFFSET fName          ;Read it back in
  2488.   call OpenFile
  2489. XEXEC:
  2490.   pop di                        ;Restore regs and return
  2491.   pop es
  2492.   pop bx
  2493.   jmp NewLine
  2494.  
  2495. SysErr:
  2496.   mov si, OFFSET sysErrMsg
  2497.   jmp Abort
  2498.  
  2499.  
  2500. EndOfProgram:
  2501. END Orig